/*
This file is part of buco.  
Copyright (c) 2012 Christopher Pawelzik <christopher@cpawelzik.com>

Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php 
*/ 
package buco.dsl;

import java.util.*;
import java.io.*;

import org.json.simple.*;
import org.json.simple.parser.*;

import com.creativewidgetworks.goldparser.engine.*;
import com.creativewidgetworks.goldparser.parser.GOLDParser;

public class BclParser {
	
	// Zum Zugriff auf die Parsing-Engine
	private GOLDParser parser; 
	
	public BclParser() {
		parser = new GOLDParser(getGrammarTable(), null, false);	
		parser.setGenerateTree(true); 
	}
	
	public List<BclConstraint> parse(String bcl) { 
		boolean noErrors = parser.parseSourceStatements(bcl);
		
		if(noErrors) { 		
			Reduction reduction = parser.getCurrentReduction(); 						
			return getCommands(reduction.get(0).asReduction()); 
		} else { 
			System.out.println(parser.getErrorMessage());
			return null;
		}
	}
	
	private List<BclConstraint> getCommands(Reduction reduction) { 
		ArrayList<BclConstraint> list = new ArrayList<BclConstraint>();
		
		// If a command has multiple objects as parameter, 
		// create on command for each object
		Reduction commandReduction = reduction.get(0).asReduction(); 
		list.addAll(getCommand(commandReduction)); 
		
		// Process more commands
		if(reduction.size() == 2) { 
			list.addAll(getCommands(reduction.get(1).asReduction())); 
		}
		
		return list; 
	}
	
	private List<BclConstraint> getCommand(Reduction reduction) { 
		ArrayList<BclConstraint> list = new ArrayList<BclConstraint>();
		String identifier = reduction.get(0).asString();
		List<JSONObject> objects = getObjects(reduction.get(1).asReduction()); 
		
		for(JSONObject object : objects) {
			BclConstraint command = new BclConstraint(identifier, object); 
			list.add(command);
		}
		
		return list;
	}
	
	/**
	 * <Objects> ::= <Object> | <Object> ',' <Objects>
	 * @param reduction
	 * @return
	 */
	private List<JSONObject> getObjects(Reduction reduction) { 
		ArrayList<JSONObject> list = new ArrayList<JSONObject>();
		JSONObject object = makeObject(reduction.get(0).asReduction()); 
		list.add(object); 

		// Add more objects
		if(reduction.size() == 3) { 
			list.addAll(getObjects(reduction.get(2).asReduction()));
		}
		
		return list; 
	}
	
	private JSONObject makeObject(Reduction objectReduction) { 
		StringBuilder sb = new StringBuilder(); 
		dump(objectReduction, sb);		
		JSONParser jsonParser = new JSONParser(); 		
		try {
			return (JSONObject) jsonParser.parse(sb.toString());
		} catch (ParseException e) {			
			e.printStackTrace();
			return null;
		}
	}
	
	private void dump(Reduction reduction, StringBuilder sb) { 
		for(Token token : reduction) {
			switch(token.getType()) { 
				case NON_TERMINAL:
					dump(token.asReduction(), sb);
					break;
				
				default:
					sb.append(token.asString());					
			}
		}
	}
	
	private InputStream getGrammarTable() { 
		InputStream is = getClass().getResourceAsStream("BCL.egt");
		if(is == null) { 
			System.out.println("Could not load grammar table."); 
		}
		return is; 
	}
}
